class RaceAi extends RaceAiPathFinding;

var float speed, crashradius, crashTime, crashOn, curveDetection, enemyCheckRadius;
var UTVehicle car;
var vector GoToPos, prevLocation;
var Vector TempPos;
var array<Actor> PathTargets;
var int checkpoint;
var Rotator direction;
var float obstacleAvoidanceLength;
var array<Vector> points;
var bool rear;
var int noPath;
var() int BackWarddistance;

var Actor prevNode, currentNode;

DefaultProperties
{
	bAdjustFromWalls=true;
	bIsPlayer=true;
	speed = 1;
	checkpoint = 0;
	obstacleAvoidanceLength = 1000;
	BackWarddistance = 1000;
	crashradius = 5;
	crashTime = 3;
	curveDetection = 3000;
	enemyCheckRadius = 100;
}

auto state Moving
{
	event Tick(float deltaTime)
	{	
		local Rotator avoid;
		local Vector prevPos;
		local Vector vel, HitLocation, HitNormal;
		local Actor traced;
		local float distance;

		

		car = UTVehicle(Pawn);

		if(RaceCar(car).FreezePawn) return;

		prevPos = GoToPos;

		direction = Rotator(GoToPos - car.Location);

		avoid = SideAvoidence(Vector(car.Rotation),obstacleAvoidanceLength,false);
		Movement(direction+avoid);


		if(Abs(VSize(car.Location - GoToPos)) < 1000){


			GoToPos = NextLocation(PathTargets[checkpoint], noPath);
		}

		if(Abs(VSize(car.Location - PathTargets[checkpoint].Location)) < 1000){
			if(checkpoint < PathTargets.Length-1)
				checkpoint++;
			else
				checkpoint=0;

			prevNode = currentNode;
			currentNode = PathTargets[checkpoint];
			`log(PathTargets.Length$" "$checkpoint);
		}

		if(noPath > 0)
		{
			GoToPos = prevPos;
		}


		vel = Vector(car.GetViewRotation()); vel.Z = 0;
		
		traced = Trace(HitLocation,HitNormal, car.Location+Normal(-vel)*400, car.Location,false);

		//controle of de bot met de achterkant tegen de muur zit
		if(traced != none && !traced.IsA('UTVehicle'))
		{
			GoToPos =  Pawn.Location + (vect(500,0,0) >> Pawn.Rotation); 
		}
		
		distance = VSize( Pawn.Location - prevLocation);
		if(WorldInfo.TimeSeconds > crashTime+crashOn || crashOn == 0)
		{
			if(distance < crashradius)
			{
				if(crashOn != 0)
				{
					ResetCar();
					crashOn=0;
				}else
					crashOn = WorldInfo.TimeSeconds;
			}
			prevLocation = Pawn.Location;
		}
		if(distance >= crashradius)
		{
			crashOn = 0;
		}
		prevLocation = Pawn.Location;
		
		CrashDetection();

		RaceTick();
	}

	function RaceTick()
	{
		//functie om aan te extenden
	}

	BEGIN:	
		car = UTVehicle(Pawn);
		PathTargets = RacePawn(car.Driver).PathTargets;
		TempPos = GoToPos;
		GoToPos = NextLocation(PathTargets[checkpoint],noPath);
}

function bool MoveBackWard()
{
	local Vector vec,hitLocation,hitNormal;
	local Actor traced;

	vec.X = BackWarddistance;
	traced = Trace(hitLocation,hitNormal,Pawn.Location+(vec >> Pawn.Rotation),Pawn.Location,true);
	return traced != none && !traced.IsA('UTVehicle');
}

function Movement(Rotator desRot){
	local float r, a, s, c;
	local bool allowBackward;
	allowBackward = MoveBackWard();

	desRot -= car.GetViewRotation();
	r = UnrRotToDeg * desRot.Yaw;
	s = Abs(VSize(car.Velocity));

	a = 45+(s/20);
	c = r; if(c < 0) c = -r; if(c > a) c = a;

	// voorkom dat de rotatie meer is als 180/-180
	while(r > 180)
		r -=360;
	while(r < -180)
		r += 360;

	// pas de snelheid aan, aan de hand van de hoek
	if(r <= -a && allowBackward){
		car.Steering = -1;
		car.Throttle = -1;
	}else if(r >= a && allowBackward){
		car.Steering = 1;
		car.Throttle = -1;
	}else if(r > 1){
		car.Steering = -1*(r/25);
		car.Throttle = 1;
	}else if(r < -1){
		car.Steering = 1*(r/-25);


		car.Throttle = 1;
	}else{
		car.Steering = 0;
		car.Throttle = 1;
	}

	if(DetectCurve(s*2))
	{
		if(s > 2000) //Remmen
		{
			car.Throttle = -1;
		}
		if(s > 1000)//Gas los
		{
			car.Throttle = 0; 
		}
	}
}

/**
 * Controleert ook voor zijkanten
 */
function Rotator SideAvoidence(Vector currentVector, float size, bool testAll = false)
{
	local Rotator avoid;
	local Vector leftLine;
	
	if(car == none)
		return  Rotator(vect(0,0,0));

	leftLine.Y = car.GetCollisionRadius();
	leftLine = Vector((Rotator(leftLine)+car.Rotation))*car.GetCollisionRadius();
	
	avoid = ObstacleAvoidence(car.Location ,currentVector,size,testAll);
	avoid += ObstacleAvoidence(car.Location+leftLine ,currentVector,size,testAll);
	avoid += ObstacleAvoidence(car.Location-leftLine ,currentVector,size,testAll);

	if(avoid.Yaw*UnrRotToDeg	 >= 45)
	{
		avoid.Yaw = 44*DegToUnrRot;
	}
	if(avoid.Yaw*UnrRotToDeg	 <= -45)
	{
		avoid.Yaw = -44*DegToUnrRot;
	}
	return avoid;
}

/**
 * Deze mehtode zorgt er voor dat bots niet tegen een muur aan lopen
 * @param currentVector - De huidige vector van het object, deze wordt aangepast volgens de obstacleavoidence
 */
function Rotator ObstacleAvoidence(Vector startloc,Vector currentVector, float size, bool testAll = false)
{
	local Vector HitLocation;
	local Vector HitNormal;
	local Vector ReturnVector;
	local Actor traced;

	traced = Trace(HitLocation,HitNormal,startloc+Normal(currentVector)*size,startloc,true);

	if(traced == none || ((traced == WorldInfo || traced.IsA('Terrain') || !traced.IsA('Pawn') && !testAll)))
		return Rotator(vect(0,0,0));
	
	ReturnVector = MirrorVectorByNormal(currentVector,HitNormal);

	return  (Rotator( currentVector + ReturnVector ) - car.Rotation);	
}

function CrashDetection()
{
	local float distance;
	distance = VSize( Pawn.Location - prevLocation);
	if(WorldInfo.TimeSeconds > crashTime+crashOn || crashOn == 0)
	{
		if(distance < crashradius)
		{
			if(crashOn != 0)
			{
				ResetCar();
				crashOn=0;
			}else
				crashOn = WorldInfo.TimeSeconds;
		}
		prevLocation = Pawn.Location;
	}
	if(distance >= crashradius)
	{
		crashOn = 0;
	}
	prevLocation = Pawn.Location;
}

function ResetCar()
{
	if(prevNode == none)
		prevNode = currentNode;
	if(EnemyNearby(prevNode.Location))
		return;
	car.Mesh.SetRBPosition(prevNode.Location);
	car.Mesh.SetRBRotation(Rotator(currentNode.Location - prevNode.Location));
	GoToPos = NextLocation(PathTargets[checkpoint],noPath);
}

function bool EnemyNearby(Vector location)
{
	local Controller C;

	foreach WorldInfo.AllControllers(class'Controller', C)
	{	
		
		if (C.bIsPlayer && !C.IsDead() && C != self)
		{
			if(VSize(location-C.Pawn.Location) > enemyCheckRadius)
			{
				return false;
			}
		}
	}
	return true;
}

/**
 * Controleert of er een bocht aanwezig is
 */
function bool DetectCurve(float speed)
{
	local Vector HitLocation;
	local Vector HitNormal;
	local Actor traced;
	local bool forward;

	forward = (Rotator( Normal(car.Velocity) ) - car.Rotation).Yaw*UnrRotToDeg < 90;
	if(!forward)
		return false;

	traced = Trace(HitLocation,HitNormal,Pawn.Location+Normal(Vector(Pawn.Rotation))*speed,Pawn.Location,true);


	if(traced == none || (traced != WorldInfo && !traced.IsA('BlockingVolume'))) return false;
	
	return true;
}